package com.github.xiaoymin.knife4j.spring.web;

import org.slf4j.LoggerFactory;
import io.swagger.models.properties.Property;
import com.github.xiaoymin.knife4j.spring.util.SwaggerUtil;
import io.swagger.models.Model;
import java.util.regex.Pattern;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSort;
import io.swagger.annotations.ApiOperation;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import java.util.Map;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.web.servlet.HandlerMapping;
import java.lang.reflect.Method;
import org.springframework.web.method.HandlerMethod;
import java.util.Set;
import org.springframework.util.ClassUtils;
import springfox.documentation.spring.web.WebMvcRequestHandler;
import com.google.common.collect.FluentIterable;
import java.util.Iterator;
import org.springframework.web.context.WebApplicationContext;
import java.util.Collections;
import java.util.Comparator;
import org.springframework.util.StringUtils;
import com.github.xiaoymin.knife4j.spring.model.SwaggerBootstrapUiPath;
import io.swagger.annotations.Api;
import com.github.xiaoymin.knife4j.spring.model.SwaggerBootstrapUiTag;
import springfox.documentation.service.Tag;
import com.google.common.collect.Lists;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.github.xiaoymin.knife4j.spring.model.SwaggerBootstrapUi;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.util.UriComponents;
import io.swagger.models.Swagger;
import springfox.documentation.service.Documentation;
import com.github.xiaoymin.knife4j.spring.model.SwaggerExt;
import com.google.common.base.Strings;
import com.github.xiaoymin.knife4j.spring.common.SwaggerBootstrapUiHostNameProvider;
import springfox.documentation.swagger.common.HostNameProvider;
import org.springframework.http.HttpStatus;
import java.util.Optional;
import springfox.documentation.spring.web.json.Json;
import org.springframework.http.ResponseEntity;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestParam;
import springfox.documentation.RequestHandler;
import com.google.common.base.Function;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMethod;
import com.github.xiaoymin.knife4j.spring.model.RestHandlerMapping;
import java.util.ArrayList;
import com.github.xiaoymin.knife4j.spring.model.MarkdownFiles;
import springfox.documentation.spi.service.RequestHandlerProvider;
import java.util.List;
import springfox.documentation.spring.web.json.JsonSerializer;
import springfox.documentation.spring.web.DocumentationCache;
import springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper;
import org.slf4j.Logger;
import springfox.documentation.annotations.ApiIgnore;
import org.springframework.stereotype.Controller;

@Controller
@ApiIgnore
public class Knife4jController
{
    private static final String DEFAULT_SORT_URL = "/v2/api-docs-ext";
    private static final String HAL_MEDIA_TYPE = "application/hal+json";
    private static final Logger LOGGER;
    private final ServiceModelToSwagger2Mapper mapper;
    private final DocumentationCache documentationCache;
    private final JsonSerializer jsonSerializer;
    private final String hostNameOverride;
    private final List<RequestHandlerProvider> handlerProviders;
    private final MarkdownFiles markdownFiles;
    private ArrayList<RestHandlerMapping> globalHandlerMappings;
    private final RequestMethod[] globalRequestMethods;
    
    @Autowired
    public Knife4jController(final Environment environment, final ServiceModelToSwagger2Mapper mapper, final DocumentationCache documentationCache, final JsonSerializer jsonSerializer, final List<RequestHandlerProvider> handlerProviders, final ObjectProvider<MarkdownFiles> markdownFilesObjectProvider) {
        this.globalHandlerMappings = new ArrayList<RestHandlerMapping>();
        this.globalRequestMethods = new RequestMethod[] { RequestMethod.POST, RequestMethod.GET, RequestMethod.PUT, RequestMethod.DELETE, RequestMethod.PATCH, RequestMethod.OPTIONS, RequestMethod.HEAD };
        this.mapper = mapper;
        this.documentationCache = documentationCache;
        this.jsonSerializer = jsonSerializer;
        this.hostNameOverride = environment.getProperty("springfox.documentation.swagger.v2.host", "DEFAULT");
        this.handlerProviders = handlerProviders;
        this.markdownFiles = markdownFilesObjectProvider.getIfAvailable();
    }
    
    private Function<RequestHandlerProvider, ? extends Iterable<RequestHandler>> handlers() {
        return (input -> input.requestHandlers());
    }
    
    @RequestMapping(value = { "/v2/api-docs-ext" }, method = { RequestMethod.GET }, produces = { "application/json", "application/hal+json" })
    @ResponseBody
    public ResponseEntity<Json> apiSorts(@RequestParam(value = "group", required = false) final String swaggerGroup, final HttpServletRequest request) {
        final String groupName = Optional.ofNullable(swaggerGroup).orElse("default");
        Documentation documentation = this.documentationCache.documentationByGroup(groupName);
        if (documentation == null) {
            Knife4jController.LOGGER.warn("Unable to find specification for group {},use default", (Object)groupName);
            documentation = this.documentationCache.documentationByGroup("default");
            if (documentation == null) {
                Knife4jController.LOGGER.warn("Unable to find specification for group default");
                return (ResponseEntity<Json>)new ResponseEntity(HttpStatus.NOT_FOUND);
            }
        }
        final Swagger swagger = this.mapper.mapDocumentation(documentation);
        UriComponents uriComponents = null;
        try {
            uriComponents = HostNameProvider.componentsFrom(request, swagger.getBasePath());
        }
        catch (Throwable var9) {
            Knife4jController.LOGGER.error(var9.getClass().getName() + ":" + var9.getMessage());
            if (var9 instanceof NoClassDefFoundError) {
                final String msg = var9.getMessage();
                if (msg != null && !"".equals(msg) && msg.endsWith("HostNameProvider")) {
                    uriComponents = SwaggerBootstrapUiHostNameProvider.componentsFrom(request, swagger.getBasePath());
                }
            }
        }
        swagger.basePath(Strings.isNullOrEmpty(uriComponents.getPath()) ? "/" : uriComponents.getPath());
        if (Strings.isNullOrEmpty(swagger.getHost())) {
            swagger.host(this.hostName(uriComponents));
        }
        this.extend(swagger);
        final SwaggerExt swaggerExt = new SwaggerExt(swagger);
        swaggerExt.setSwaggerBootstrapUi(this.initSwaggerBootstrapUi(request, documentation, swaggerExt));
        return (ResponseEntity<Json>)new ResponseEntity((Object)this.jsonSerializer.toJson((Object)swaggerExt), HttpStatus.OK);
    }
    
    private SwaggerBootstrapUi initSwaggerBootstrapUi(final HttpServletRequest request, final Documentation documentation, final SwaggerExt swaggerExt) {
        final SwaggerBootstrapUi swaggerBootstrapUi = new SwaggerBootstrapUi();
        final WebApplicationContext wc = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());
        if (wc == null) {
            final String msg = "WebApplicationContext is Empty~!,Enable SwaggerBootstrapUi fun fail~!";
            Knife4jController.LOGGER.warn(msg);
            swaggerBootstrapUi.setErrorMsg(msg);
            return swaggerBootstrapUi;
        }
        final Iterator<Tag> tags = documentation.getTags().iterator();
        this.initGlobalRequestMappingArray(swaggerExt);
        final List<SwaggerBootstrapUiTag> targetTagLists = Lists.newArrayList();
        final ArrayList targetPathLists = Lists.newArrayList();
        while (tags.hasNext()) {
            final Tag sourceTag = tags.next();
            final String tagName = sourceTag.getName();
            final int order = Integer.MAX_VALUE;
            final SwaggerBootstrapUiTag tag = new SwaggerBootstrapUiTag(Integer.valueOf(order));
            tag.name(tagName).description(sourceTag.getDescription());
            Api tagApi = null;
            RestHandlerMapping tagMapping = null;
            for (final RestHandlerMapping rhm : this.globalHandlerMappings) {
                final Api api = rhm.getBeanType().getAnnotation(Api.class);
                if (api != null) {
                    api.tags();
                    if (api.tags().length > 0) {
                        if (Lists.newArrayList((Object[])api.tags()).contains(tagName)) {
                            tagApi = api;
                            tagMapping = rhm;
                            this.createPathInstance(rhm, targetPathLists);
                        }
                        else {
                            final String firstTag = api.tags()[0];
                            if (!StringUtils.isEmpty((Object)firstTag) || !this.checkExists(tagName, rhm.getBeanType())) {
                                continue;
                            }
                            tagApi = api;
                            tagMapping = rhm;
                            this.createPathInstance(rhm, targetPathLists);
                        }
                    }
                    else {
                        if (!this.checkExists(tagName, rhm.getBeanType())) {
                            continue;
                        }
                        if (!StringUtils.isEmpty((Object)api.value())) {
                            tag.name(api.value());
                        }
                        tagApi = api;
                        tagMapping = rhm;
                        this.createPathInstance(rhm, targetPathLists);
                    }
                }
                else {
                    if (!this.checkExists(tagName, rhm.getBeanType())) {
                        continue;
                    }
                    tagMapping = rhm;
                    this.createPathInstance(rhm, targetPathLists);
                }
            }
            if (tagMapping != null) {
                tag.setOrder(Integer.valueOf(this.getRestTagOrder(tagMapping.getBeanType(), tagApi)));
            }
            targetTagLists.add(tag);
        }
        Collections.sort(targetTagLists, new Comparator<SwaggerBootstrapUiTag>() {
            @Override
            public int compare(final SwaggerBootstrapUiTag o1, final SwaggerBootstrapUiTag o2) {
                return o1.getOrder().compareTo(o2.getOrder());
            }
        });
        targetPathLists.sort(new Comparator<SwaggerBootstrapUiPath>() {
            @Override
            public int compare(final SwaggerBootstrapUiPath o1, final SwaggerBootstrapUiPath o2) {
                return o1.getOrder().compareTo(o2.getOrder());
            }
        });
        swaggerBootstrapUi.setTagSortLists((List)targetTagLists);
        swaggerBootstrapUi.setPathSortLists((List)targetPathLists);
        if (this.markdownFiles != null) {
            swaggerBootstrapUi.setMarkdownFiles(this.markdownFiles.getMarkdownFiles());
        }
        return swaggerBootstrapUi;
    }
    
    private void initGlobalRequestMappingArray(final SwaggerExt swaggerExt) {
        if (this.globalHandlerMappings.size() == 0) {
            String parentPath = "";
            if (!StringUtils.isEmpty((Object)swaggerExt.getBasePath()) && !"/".equals(swaggerExt.getBasePath())) {
                parentPath += swaggerExt.getBasePath();
            }
            try {
                final List<RequestHandler> requestHandlers = (List<RequestHandler>)FluentIterable.from((Iterable)this.handlerProviders).transformAndConcat((Function)this.handlers()).toList();
                for (final RequestHandler requestHandler : requestHandlers) {
                    if (requestHandler instanceof WebMvcRequestHandler) {
                        final WebMvcRequestHandler webMvcRequestHandler = (WebMvcRequestHandler)requestHandler;
                        final Set<String> patterns = (Set<String>)webMvcRequestHandler.getRequestMapping().getPatternsCondition().getPatterns();
                        final Set<RequestMethod> restMethods = (Set<RequestMethod>)webMvcRequestHandler.getRequestMapping().getMethodsCondition().getMethods();
                        final HandlerMethod handlerMethod = webMvcRequestHandler.getHandlerMethod();
                        final Class<?> controllerClazz = (Class<?>)ClassUtils.getUserClass(handlerMethod.getBeanType());
                        final Method method = ClassUtils.getMostSpecificMethod(handlerMethod.getMethod(), (Class)controllerClazz);
                        for (final String url : patterns) {
                            if (Knife4jController.LOGGER.isDebugEnabled()) {
                                Knife4jController.LOGGER.debug("url:" + url + "\r\nclass:" + controllerClazz.toString() + "\r\nmethod:" + method.toString());
                            }
                            this.globalHandlerMappings.add(new RestHandlerMapping(parentPath + url, (Class)controllerClazz, method, (Set)restMethods));
                        }
                    }
                }
            }
            catch (Exception var6) {
                Knife4jController.LOGGER.error(var6.getMessage(), (Throwable)var6);
            }
        }
    }
    
    @Deprecated
    private void initGlobalRequestMappingArray(final WebApplicationContext wc, final SwaggerExt swaggerExt) {
        if (this.globalHandlerMappings.size() == 0) {
            String parentPath = "";
            if (!StringUtils.isEmpty((Object)swaggerExt.getBasePath()) && !"/".equals(swaggerExt.getBasePath())) {
                parentPath += swaggerExt.getBasePath();
            }
            final Map<String, HandlerMapping> requestMappings = (Map<String, HandlerMapping>)BeanFactoryUtils.beansOfTypeIncludingAncestors((ListableBeanFactory)wc, (Class)HandlerMapping.class, true, false);
            for (final HandlerMapping handlerMapping : requestMappings.values()) {
                if (handlerMapping instanceof RequestMappingHandlerMapping) {
                    final RequestMappingHandlerMapping rmhMapping = (RequestMappingHandlerMapping)handlerMapping;
                    final Map<RequestMappingInfo, HandlerMethod> handlerMethods = (Map<RequestMappingInfo, HandlerMethod>)rmhMapping.getHandlerMethods();
                    for (final RequestMappingInfo rmi : handlerMethods.keySet()) {
                        final PatternsRequestCondition prc = rmi.getPatternsCondition();
                        final Set<RequestMethod> restMethods = (Set<RequestMethod>)rmi.getMethodsCondition().getMethods();
                        final Set<String> patterns = (Set<String>)prc.getPatterns();
                        final HandlerMethod handlerMethod = handlerMethods.get(rmi);
                        for (final String url : patterns) {
                            final Class clazz = ClassUtils.getUserClass(handlerMethod.getBeanType());
                            final Method method = ClassUtils.getMostSpecificMethod(handlerMethod.getMethod(), clazz);
                            if (Knife4jController.LOGGER.isDebugEnabled()) {
                                Knife4jController.LOGGER.debug("url:" + url + "\r\nclass:" + clazz.toString() + "\r\nmethod:" + method.toString());
                            }
                            this.globalHandlerMappings.add(new RestHandlerMapping(parentPath + url, clazz, method, (Set)restMethods));
                        }
                    }
                }
            }
        }
    }
    
    private void createPathInstance(final RestHandlerMapping rhm, final List<SwaggerBootstrapUiPath> targetPathLists) {
        if (rhm.getRequestMethods() != null && rhm.getRequestMethods().size() != 0) {
            for (final RequestMethod requestMethod : rhm.getRequestMethods()) {
                targetPathLists.add(new SwaggerBootstrapUiPath(rhm.getUrl(), requestMethod.name().toUpperCase(), Integer.valueOf(this.getRestMethodOrder(rhm.getBeanOfMethod()))));
            }
        }
        else {
            for (final RequestMethod requestMethod2 : this.globalRequestMethods) {
                targetPathLists.add(new SwaggerBootstrapUiPath(rhm.getUrl(), requestMethod2.name().toUpperCase(), Integer.valueOf(this.getRestMethodOrder(rhm.getBeanOfMethod()))));
            }
        }
    }
    
    private int getRestTagOrder(final Class<?> aClass, final Api api) {
        int order = Integer.MAX_VALUE;
        if (api != null) {
            final int post = api.position();
            if (post == 0) {
                if (aClass != null) {
                    final ApiSort annotation = ClassUtils.getUserClass((Class)aClass).getAnnotation(ApiSort.class);
                    if (annotation != null) {
                        order = annotation.value();
                    }
                }
            }
            else {
                order = post;
            }
        }
        else if (aClass != null) {
            final ApiSort annotation2 = ClassUtils.getUserClass((Class)aClass).getAnnotation(ApiSort.class);
            if (annotation2 != null) {
                order = annotation2.value();
            }
        }
        return order;
    }
    
    private int getRestMethodOrder(final Method target) {
        int pathOrder = Integer.MAX_VALUE;
        final ApiOperation apiOperation = target.getAnnotation(ApiOperation.class);
        if (apiOperation != null) {
            if (apiOperation.position() != 0) {
                pathOrder = apiOperation.position();
            }
            else {
                final ApiOperationSort apiOperationSort = target.getAnnotation(ApiOperationSort.class);
                if (apiOperationSort != null) {
                    pathOrder = apiOperationSort.value();
                }
            }
        }
        else {
            final ApiOperationSort apiOperationSort = target.getAnnotation(ApiOperationSort.class);
            if (apiOperationSort != null) {
                pathOrder = apiOperationSort.value();
            }
        }
        return pathOrder;
    }
    
    private boolean checkExists(final String tagName, final Class<?> aClass) {
        boolean flag = false;
        if (!StringUtils.isEmpty((Object)tagName)) {
            final String regexStr = tagName.replaceAll("-", ".*?");
            final Pattern pattern = Pattern.compile(regexStr, 2);
            if (pattern.matcher(aClass.getSimpleName()).matches()) {
                flag = true;
            }
        }
        return flag;
    }
    
    private String hostName(final UriComponents uriComponents) {
        if (!"DEFAULT".equals(this.hostNameOverride)) {
            return this.hostNameOverride;
        }
        final String host = uriComponents.getHost();
        final int port = uriComponents.getPort();
        if (port > -1) {
            return String.format("%s:%d", host, port);
        }
        return host;
    }
    
    private void extend(final Swagger swagger) {
        for (final Map.Entry<String, Model> entry : swagger.getDefinitions().entrySet()) {
            final Model model = entry.getValue();
            final String key = entry.getKey();
            if (key.contains("ApiResult") && !SwaggerUtil.getRealType(key).contains("ApiResult")) {
                final Map<String, Property> props = (Map<String, Property>)model.getProperties();
                final Property dataProp = props.get("data");
                final Property newProp = SwaggerUtil.getNewProp(dataProp, SwaggerUtil.getRealType(key), swagger.getDefinitions());
                props.put("data", newProp);
            }
        }
    }
    
    static {
        LOGGER = LoggerFactory.getLogger((Class)Knife4jController.class);
    }
}
